
#include <assert.h>
#include <gfx/gl20/rendertargetfbo.h>
#include <gfx/gl20/texture.h>
//#include <strings.h>
#include <string.h>


ceRenderTargetFBO::ceRenderTargetFBO()
  : _colorTexture(0)
  , _depthTexture(0)
{
  CE_OBJECT_CONSTR;
}

bool ceRenderTargetFBO::Initialize(iDevice* device, const ceRenderTargetDescriptor& desc)
{
  CE_CHECK_GL_ERROR;
  _size = desc.Size;
  glGenFramebuffersEXT(1, &_fboName);
  if (_fboName == 0)
    {
      return false;
    }
  CE_CHECK_GL_ERROR;

  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, _fboName);

  // create the texture buffer
  ceTextureDescriptor tDesc;
  memset(&tDesc, 0, sizeof(tDesc));
  tDesc.anisotropic = 1.0f;
  tDesc.format = desc.ColorFormat;
  tDesc.mipMap = false;
  tDesc.size = desc.Size;
  _colorTexture = device->CreateTexture2D(tDesc);
  if (!_colorTexture)
    {
      // TODO: Cleanup
      glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
      return false;
    }

  ceTexture2D* txt2d = static_cast<ceTexture2D*>(_colorTexture);
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, txt2d->GetName(), 0);


  CE_CHECK_GL_ERROR;
  if (desc.DepthStencilTexture)
    {
      // create the texture buffer
      ceTextureDescriptor tDesc;
      memset(&tDesc, 0, sizeof(tDesc));
      tDesc.anisotropic = 1.0f;
      tDesc.format = desc.DepthStencilFormat;
      tDesc.mipMap = false;
      tDesc.size = desc.Size;
      _depthTexture = device->CreateTexture2D(tDesc);
      CE_CHECK_GL_ERROR;
      if (!_depthTexture)
        {
          // TODO: Cleanup
          glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
          CE_CHECK_GL_ERROR;
          return false;
        }

      ceTexture2D* txt2d = static_cast<ceTexture2D*>(_depthTexture);
      switch (desc.DepthStencilFormat)
        {
        case PF_D24:
          glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, txt2d->GetName(), 0);
          break;
        case PF_D24S8:
          glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, txt2d->GetName(), 0);
          glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, txt2d->GetName(), 0);
          break;
        default:
          assert (false);
        }
      CE_CHECK_GL_ERROR;
   }
  else
    {
      // create the depth buffer 
      CE_CHECK_GL_ERROR;
      glGenRenderbuffersEXT(1, &_depthStencilRenderbuffer);
      glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depthStencilRenderbuffer);
      switch (desc.DepthStencilFormat)
        {
        case PF_D24:
          glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, desc.Size.w, desc.Size.h);
          glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _depthStencilRenderbuffer);
          break;

        case PF_D24S8:
          glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, desc.Size.w, desc.Size.h);
          glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _depthStencilRenderbuffer);
          glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _depthStencilRenderbuffer);
          break;

        default:
          assert (false);
        }
      CE_CHECK_GL_ERROR;

  }



  GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  CE_CHECK_GL_ERROR;
  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
  CE_CHECK_GL_ERROR

  
  if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
    {
      return true;
    }

  printf ("RenderTargetFBO: FBO incomplete: 0x%08x\n", status);
  fflush (stdout);

  // TODO: Cleanup
  return false;
}

void ceRenderTargetFBO::Bind()
{
  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, _fboName);
}


void ceRenderTargetFBO::Unbind()
{
  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
}

int ceRenderTargetFBO::GetX() const
{
  return 0;
}

int ceRenderTargetFBO::GetY() const
{
  return 0;
}

unsigned ceRenderTargetFBO::GetWidth() const
{
  return _size.w;
}

unsigned ceRenderTargetFBO::GetHeight() const
{
  return _size.h;
}

const ceRect& ceRenderTargetFBO::GetSize() const
{
  return _size;
}

ceRenderTargetMethod ceRenderTargetFBO::GetMethod() const
{
  return RTM_FBO;
}

iTexture2D* ceRenderTargetFBO::GetColorTexture() const
{
  return _colorTexture;
}

iTexture2D* ceRenderTargetFBO::GetDepthTexture() const
{
  return _depthTexture;
}

